package src.com.jun.spring;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 咏鹅
 * @version 1.0
 * @description TODO
 * @date 2024/3/16 23:47
 */
public class SpringApplicationContext {
    private final Class configClass;

    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();


    public SpringApplicationContext(Class configClass) {
        this.configClass = configClass;
        //扫描是否存在ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //拿到类的绝对路径
            String path = componentScanAnnotation.value().replace(".", "/");
            ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            //断言，不允许resource 为空
            assert resource != null;
            File file = new File(resource.getFile());

            //file 是文件夹才进入
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                //遍历 files 下的文件，找到与上方类的绝对路径,进行编译后路径拼接
                for (File f : files) {

                    String fileName = f.getName();
                    String className = fileName.substring(0, fileName.lastIndexOf("."));
                    //包的路径+类名
                    String packageName = configClass.getPackage().getName() + "." + className;

                    try {
                        //加载这个类，如果有Component注解则注入bean
                        Class<?> clazz = classLoader.loadClass(packageName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //Bean

                            //获取被标记对应的类  如果UserService 标记了 Component 那么在这儿获取的就是UserService
                            Component component = clazz.getAnnotation(Component.class);
                            String beanName = component.value();

                            //获取Scope 标记 Bean对象
                            BeanDefinition beanDefinition = getBeanDefinition(clazz);

                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        //实例化bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //单例bean
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBane(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            } else {

            }
        }


    }

    private Object createBane(String beanName, BeanDefinition beanDefinition) {

        Class clazz = beanDefinition.getType();
        try {
            return clazz.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private BeanDefinition getBeanDefinition(Class<?> clazz) {
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setType(clazz);

        //获取Scope注解
        if (clazz.isAnnotationPresent(Scope.class)) {
            Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
            beanDefinition.setScope(scopeAnnotation.value());

        } else {
            //默认单例
            beanDefinition.setScope("singleton");
        }
        return beanDefinition;
    }

    public Object getBean(String beanName) {

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        } else {
            String scope = beanDefinition.getScope();
            if (scope.equals("singleton")) {
                Object bean = singletonObjects.get(beanName);
                if (bean == null) {
                    Object o = createBane(beanName, beanDefinition);
                    assert o != null;
                    singletonObjects.put(beanName, o);
                }
                return bean;
            } else {
                return createBane(beanName, beanDefinition);
            }
        }
    }

}
